package breakthrough;

import java.util.ArrayList;

import game.GameMove;
import game.GamePlayer;
import game.GameState;
import game.Util;

public class MiniMaxBreakthroughPlayer extends RandomBreakthroughPlayer {

	public static int MAX_SCORE = Integer.MAX_VALUE;
	private static int DEPTH = 3;
	public final int MAX_DEPTH = 50;
	public int depthLimit;
	
	
	protected ScoredBreakthroughMove[] mvStack;
	public Heuristic h;
	
	public MiniMaxBreakthroughPlayer(String n, Heuristic h) {
		super(n);
		this.h = h;
		// TODO Auto-generated constructor stub
	}
	
	protected ArrayList<BreakthroughMove> getMoveList(GameState state)
	{
		BreakthroughState board = (BreakthroughState)state;
		ArrayList<BreakthroughMove> list = new ArrayList<BreakthroughMove>();  
		BreakthroughMove mv = new BreakthroughMove();
		int dir = state.who == GameState.Who.HOME ? +1 : -1;
		for (int r=0; r<BreakthroughState.N; r++) {
			for (int c=0; c<BreakthroughState.N; c++) {
				mv.startRow = r;
				mv.startCol = c;
				mv.endingRow = r+dir; mv.endingCol = c;
				if (board.moveOK(mv)) {
					list.add((BreakthroughMove)mv.clone());
				}
				mv.endingRow = r+dir; mv.endingCol = c+1;
				if (board.moveOK(mv)) {
					list.add((BreakthroughMove)mv.clone());
				}
				mv.endingRow = r+dir; mv.endingCol = c-1;
				if (board.moveOK(mv)) {
					list.add((BreakthroughMove)mv.clone());
				}
			}
		}
		return list;
	}
	
	protected static void shuffle (ArrayList<BreakthroughMove> ary)
	{
		int len = ary.size();
		for (int i=0; i<len; i++) {
			int spot = Util.randInt(i, len-1);
			BreakthroughMove tmp = ary.get(i);
			ary.set(i,ary.get(spot));
			ary.set(spot,tmp);
		}	
	}

	public void init()
	{
		mvStack = new ScoredBreakthroughMove [MAX_DEPTH];
		for (int i=0; i<MAX_DEPTH; i++) {
			mvStack[i] = new ScoredBreakthroughMove(new BreakthroughMove(0,0,0,0),0);
		}
	}
	
	protected boolean terminalValue(GameState brd, ScoredBreakthroughMove mv, int depth)
	{
		GameState.Status status = brd.getStatus();
		boolean isTerminal = true;
		
		if (status == GameState.Status.HOME_WIN) {
			mv.set(new BreakthroughMove(0,0,0,0), MAX_SCORE);
		} else if (status == GameState.Status.AWAY_WIN) {
			mv.set(new BreakthroughMove(0,0,0,0), - MAX_SCORE);
		} else if (depth < DEPTH) {
			mv.set(new BreakthroughMove(0,0,0,0), 0);
		} else {
			isTerminal = false;
		}
		return isTerminal;
		
	}

	protected static void shuffle(int [] ary)
	{
		int len = ary.length;
		for (int i=0; i<len; i++) {
			int spot = Util.randInt(i, len-1);
			int tmp = ary[i];
			ary[i] = ary[spot];
			ary[spot] = tmp;
		}
	}

	
	private void minimax(BreakthroughState brd, int currDepth)
	{
		boolean toMaximize = (brd.getWho() == GameState.Who.HOME);
		boolean isTerminal = terminalValue(brd, mvStack[currDepth], currDepth);
		
		if (isTerminal) {
			;
		} else if (currDepth == depthLimit) {
			mvStack[currDepth].set(new BreakthroughMove(0,0,0,0), h.calc(brd));
		} else {
			ScoredBreakthroughMove tempMv = new ScoredBreakthroughMove(new BreakthroughMove(0,0,0,0), 0);

			double bestScore = (brd.getWho() == GameState.Who.HOME ? 
												Double.NEGATIVE_INFINITY :
												Double.POSITIVE_INFINITY);
			ScoredBreakthroughMove bestMove = mvStack[currDepth];
			ScoredBreakthroughMove nextMove = mvStack[currDepth+1];
			
			bestMove.set(new BreakthroughMove(0,0,0,0), bestScore);

			ArrayList<BreakthroughMove> moves  = getMoveList(brd);
			
			shuffle(moves);
			
			for (BreakthroughMove move : moves) {
				// Make move on board
				tempMv.move = move;
				BreakthroughState nBrd = (BreakthroughState) brd.clone();
				nBrd.makeMove(tempMv.move);
				
				// Check out worth of this move
				minimax(brd, currDepth+1);
				
				// Check out the results, relative to what we've seen before
				if (toMaximize && nextMove.score > bestMove.score) {
					bestMove.set(move, nextMove.score);
				} else if (!toMaximize && nextMove.score < bestMove.score) {
					bestMove.set(move, nextMove.score);
				}
			}
			mvStack[0] = bestMove;
		}
	}	
	public GameMove getMove(GameState currState, String lastMove)
	{
		minimax((BreakthroughState) currState, 0);
		return mvStack[0].move;
	}
	
	
	public static void main(String[] args)
	{
		GamePlayer p = new MiniMaxBreakthroughPlayer("MiniMax BT", new Heuristic());
		p.compete(args);
	}
	
}
